﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using cmdLineParserModule.HelperClasses;

namespace cmdLineParserModule
{
    /// <summary>
    /// Main module class used for filling object with data according to command line parameters
    /// </summary>
    public class cmdLineParser
    {
        /// <summary>
        /// Prints usage in human readable format
        /// </summary>
        /// <param name="choices">Object with marked properties</param>
        public void printUsage(object choices)
        {
            if (choices == null)
                return;

            Type type = choices.GetType();
            FieldInfo[] fields = type.GetFields();
            var optionedFields = TypeManager.getOptionedFields(fields);

            Console.WriteLine("Usage:\n");

            string formatString = " {0, -30} {1}\n";

            foreach (var field in optionedFields)
            {
                optionAttribute attributeClass = field.Value;
                Console.WriteLine(formatString, attributeClass.getUsageText(attributeClass.command), attributeClass.description);

                string altInfo = "The same as " + attributeClass.command;

                if (attributeClass.alternatives != null)
                {
                    foreach (string alternativeCmd in attributeClass.alternatives)
                    { Console.WriteLine(formatString, attributeClass.getUsageText(alternativeCmd), altInfo);}
                }
            }

        }

        /// <summary>
        /// Fills choices properties according to commands. Throws WrongDefinitionException when programmer
        /// made a mistake, WrongUsageException when user made a mistake.
        /// </summary>
        /// <param name="choices">Class with properties marked with option* class</param>
        /// <param name="args">Command line arguments</param>
        public void parse(object choices, string[] args)
        {
            string[] ordinaryParams;
            parse(choices, args, out ordinaryParams);
        }

        /// <summary>
        /// Fills choices properties according to commands. Throws WrongDefinitionException when programmer
        /// made a mistake, WrongUsageException when user made a mistake.
        /// </summary>
        /// <param name="choices">Class with properties marked with option* class</param>
        /// <param name="args">Command line arguments</param>
        /// <param name="ordinaryParameters">List of ordinary parameters</param>
        public void parse(object choices, string[] args, out string[] ordinaryParameters)
        {
            TokenManager tokenManager = new TokenManager(args);
            
            // converts args to tokens (contains additional info)

            // gets all properties of args marked with some optionAttribute class
            FieldInfo[] allFields = choices.GetType().GetFields();
            var optionedFields = TypeManager.getOptionedFields(allFields);

            foreach (var pair in optionedFields)
            {
                FieldInfo field = pair.Key;
                optionAttribute option = pair.Value;
                option.field = field;

                // checks if option is well-defined
                option.check();

                // adds command and alternatives of this option (for duplicity checking)
                tokenManager.addUseOf(option);


                cmdLineToken choiceToken;
                cmdLineToken paramToken;

                tokenManager.findChoiceAndParamToken(option.command, option.alternatives, out choiceToken, out paramToken);

                // Fills option field according to choiceToken and paramToken
                option.parse(choices, choiceToken, paramToken);

            }

            // after-work:

            // gets list of unused tokens = ordinary parameters
            ordinaryParameters = tokenManager.ordinaryParameters();

            // looks for unknown choices entered by user
            tokenManager.checkPendingChoices();

        }

    }
}
